function DockHolder(p){
	this.elem = document.createElement('div');
	this.elem.className = 'dockholder';
	_addClassName(this.elem, 'animated');
	if (p)
	    p.appendChild(this.elem);
	this.numChild = 0;
	this.first = null;
	this.second = null;
	this.curMode = 0;
	this.spliter = null;
	this.percent = 50;
	this.holder = null;
}

DockHolder.prototype.addPanel = function(dockPanel, mode, idx){
	with(this){
		if (numChild==0){
			first = dockPanel;
			elem.appendChild(dockPanel.elem);
			dockPanel.holder = this;
			with (first.elem.style){
				left = top = '0%';
				width = height = '100%';
			}
			numChild++;
		}else{
			//1 or 2 children
			if (dockPanel.holder && dockPanel.holder!=this) dockPanel.holder.detachPanel(dockPanel);
			elem.appendChild(dockPanel.elem);
			dockPanel.holder = this;
			if (dockPanel == (idx==0?first:second)){
				if (curMode == mode) return;//nothing to do, old formation
			}else{
				numChild++;
			}
			if (idx==0){
				if (dockPanel!=first){
					second = first;
					first = dockPanel;
				}
			}else{
				if (dockPanel==first){
					first = second;
				}
				second = dockPanel;					
			}
			if (mode==0){
				first.elem.style.left = 0; second.elem.style.left = '50%';
				first.elem.style.top = second.elem.style.top = 0;
				first.elem.style.width = second.elem.style.width = '50%';//common_holder.clientWidth/2-34+'px';// '50%';
				first.elem.style.height = second.elem.style.height = '100%';//common_holder.clientHeight-34+'px';// '100%';
			}else{
				first.elem.style.left = second.elem.style.left = 0;
				first.elem.style.top = 0; second.elem.style.top = '50%';
				first.elem.style.width = second.elem.style.width = '100%';//common_holder.clientWidth-34+'px';// ''100%';
				first.elem.style.height = second.elem.style.height = '50%';//common_holder.clientHeight/2-34+'px';//'50%';		
			}
			
			//add spliter
			if (!spliter){
				spliter = document.createElement('div');			
				elem.appendChild(spliter);
				//spliter.draggable = true;
				var upval = this;
				var tf = function(e){
					with(upval){
						var pos = _absPos(elem);
						var percent;
						if (curMode==0){
							percent = ((e.clientX-pos.x)/elem.clientWidth*100);
						}else{
							percent = ((e.clientY-pos.y)/elem.clientHeight*100);
						}
						setSpliter(percent);
					}
				}
				var func;
				if (_browser.is_mobile){
					func = function(ev){
						if (ev.targetTouches.length!=1) return;
						ev.preventDefault();
						var e = ev.targetTouches[0];
						tf(e);
					}
				}else{
					func = tf;
				}
				if (_browser.is_mobile){
					spliter.addEventListener('touchstart',function(e){
						if (first) _removeClassName(first.elem,'animated');
						if (second) _removeClassName(second.elem,'animated');
						elem.addEventListener('touchmove',func,false);
					},false);
					elem.addEventListener('touchend',function(e){
						elem.removeEventListener('touchmove',func,false);
						if (first) _addClassName(first.elem,'animated');
						if (second) _addClassName(second.elem,'animated');
					},false);
				}else{					
					spliter.addEventListener('mousedown',function(e){
						e.preventDefault();
						if (first) _removeClassName(first.elem,'animated');
						if (second) _removeClassName(second.elem,'animated');
						elem.addEventListener('mousemove',func,false);
					},false);
					elem.addEventListener('mouseup',function(e){
						elem.removeEventListener('mousemove',func,false);
						if (first) _addClassName(first.elem,'animated');
						if (second) _addClassName(second.elem,'animated');
					},false);
				}
			}
			spliter.style.cssText = "";
			spliter.className = mode==0?'dockspliterH':'dockspliterV';
		}
		curMode = mode;
	}
}

DockHolder.prototype.replacePanel = function(newPanel, panel){
    with(this){
		if (panel == first){
			first = newPanel;
		}else{
			second = newPanel;
		}
		with(newPanel.elem.style){
			top = panel.elem.style.top;
			left = panel.elem.style.left;
			width = panel.elem.style.width;
			height = panel.elem.style.height;
		}	
		elem.replaceChild(newPanel.elem, panel.elem);
		newPanel.holder = this;
		panel.holder = null;
    }
}

DockHolder.prototype.detachPanel = function(dockPanel){
	with(this){
		if (dockPanel==first){
			first = second;
		}
		second = null;
		elem.removeChild(dockPanel.elem);
		numChild--;
		if (numChild==0){
			//remove itself
			if (holder){
			    holder.detachPanel(this);
			}else
			    elem.parentNode.removeChild(elem);
			return;
		}
		//from here, numChild should be 1
		elem.removeChild(spliter);
		spliter = null;		
		//reposition
		with (first.elem.style){
			left = top = '0%';
			width = height = '100%';
		}
		/*
		if (first instanceof DockHolder && first.numChild == 1){
			replacePanel(first.first, first);
		}*/
	}
}

DockHolder.prototype.setSpliter= function(percent){
	this.percent = percent;
    with(this){
		if (numChild<2) return;
		if (curMode==0){
			var split = percent+'%';
			first.elem.style.width = split;
			spliter.style.left = split;
			second.elem.style.left = split;
			second.elem.style.width = (100-percent)+'%';
		}else{
			var split = percent+'%';
			first.elem.style.height = split;
			spliter.style.top = split;
			second.elem.style.top = split;
			second.elem.style.height = (100-percent)+'%';							
		}
    }
}

DockHolder.prototype.getSpliter = function(){
	return this.percent;
}

function DockPanel(panel){
	this.elem = panel;//document.createElement('div');
	this.content = panel.querySelector('section');
	if (this.content){
		UI.makePannable(this.content);
	}
	_Dock.addPanel(this);
	//this.elem.className = 'dockpanel';
	_addClassName(this.elem,'dockpanel');
	_addClassName(this.elem,'animated');
	this.header = this.elem.querySelector('header');
	if (!_browser.is_mobile)
		this.header.draggable = true;
	this.holder = null;
	//this.holder = new DockHolder(panel.parentNode);
	//this.holder.addPanel(this,0,0);
	
	var upval = this;
	var tf = function(e, px, py){
		with(_DnD){
			if (clip==null) return true;
			if (clip.elem==this) return true;
			if (!_hasClassName(clip.elem,'dockpanel')) return true;
			//put highlight
			if (hl == null){
				hl = document.createElement('div');
				hl.className = 'dockpos';
			}
			//repositioning
			var this_pos = _absPos(this);
			var offsetX = px - this_pos.x;
			var offsetY = py - this_pos.y;
			//0 0 - A B: Bx - Ay = 0
			//A 0 - 0 B: Bx + Ay - AB = 0
			
			var line1 = this.clientHeight * offsetX - this.clientWidth * offsetY;
			var line2 = this.clientHeight * offsetX + this.clientWidth * offsetY
				- this.clientWidth * this.clientHeight;
			if (line1>0){
				if (line2>0){
					mode = 0;
					idx = 1;
				}else{
					mode = 1;
					idx = 0;
				}
			}else{
				if (line2>0){
					mode = 1;
					idx = 1;
				}else{
					mode = 0;
					idx = 0;
				}
			}
			var hls = hl.style;
			if (mode==0){
				hls.left = idx==0?'0%':'50%';
				hls.top = '0%';
				hls.width = '50%';
				hls.height = '100%';
			}else{
				hls.left = '0%';
				hls.top = idx==0?'0%':'50%';
				hls.width = '100%';
				hls.height = '50%';				
			}
			var log = document.getElementById('log');
			//log.innerHTML = offsetX + ' : ' + e.target.className +':'+line1+':'+line2;
			this.appendChild(hl);
			e.preventDefault();
			return false;
		}
		return true;
	}
	if (_browser.is_mobile){
	    this.header.addEventListener('touchstart', function(e){
			e.preventDefault();
		    _DnD.clip = upval;
		    upval.elem.style.opacity = '0.4';
			var _all_panels = document.querySelectorAll('.dockpanel');
			var poses = [];
			[].forEach.call(_all_panels, function(panel) {
				var pos = _absPos(panel);
				poses.push({
					l: pos.x,
					t: pos.y,
					r: pos.x + panel.clientWidth,
					b: pos.y + panel.clientHeight,
					ref: panel
				});
			},false);
			var touchmoveF = function(e){
				if (e.targetTouches.length!=1) return;
				e.preventDefault();
				var pos = {
					x: e.targetTouches[0].clientX,
					y: e.targetTouches[0].clientY
				};
				with(_DnD){					
					drop = null;
					poses.forEach(function(panel){
						if (drop) return;
						if (panel.l <= pos.x && pos.x <= panel.r
							&& panel.t <= pos.y && pos.y <= panel.b){
							drop = panel;
						}
					});
					if (drop && drop.ref != clip.elem)
						tf.call(drop.ref, e, pos.x, pos.y);
					else//equal to drag leave
						if (hl)	clearHL();
				}
			}
			_root.addEventListener('touchmove', touchmoveF, false);
			_root.addEventListener('touchend', function(e){
				_root.removeEventListener('touchmove', touchmoveF, false);	
				with(_DnD){
					if (clip){
						if (hl) clearHL();
						clip.elem.style.opacity = '1';
						if (drop && drop.ref!=clip.elem)
							_Dock.getPanel(drop.ref).dropPanel(clip, mode, idx);
						clip = null;
					}
				}
			},false);
		},false);
	}else{
	    this.header.addEventListener('dragstart', function(e){
		    _DnD.clip = upval;
		    e.dataTransfer.setDragImage(upval.elem, e.offsetX, e.offsetY);
			e.dataTransfer.setData("text/html", upval.header.innerHTML);
			e.dataTransfer.effectAllowed = "move";
		    upval.elem.style.opacity = '0.4';
	    },false);
		this.header.addEventListener('dragend', function(e){
			_DnD.clearHL();
			upval.elem.style.opacity = '1';
		},false);
		this.elem.addEventListener('dragover', function(e){
		    e.dataTransfer.dropEffect = "move";
			return tf.apply(this, [e, e.clientX, e.clientY]);
		},false);
		this.elem.addEventListener('drop', function(e){
			with(_DnD){
				if (clip){
					upval.dropPanel(clip, mode, idx);
					_DnD.clearHL();
					clip = null;					
					e.stopPropagation(); // stops the browser from redirecting.
					return false;
				}
			}
		},false);
	}

	this.elem.addEventListener('dragleave', function(e){
		with(_DnD){
			if (!clip) return;
			if (clip==upval) return;
			if (!_hasClassName(clip.elem,'dockpanel')) return;
			if (!hl) return;
			if (hl.parentNode != this) return;
			//check coordinate for actual leave
			var this_pos = _absPos(this);
			var offsetX = e.clientX - this_pos.x;
			var offsetY = e.clientY - this_pos.y;
			//offset by 1 to detect leave at the very border
			if (offsetX <= 1 || offsetX >= this.clientWidth-1 ||
				offsetY <= 1 || offsetY >= this.clientHeight-1){
				this.removeChild(hl);
				hl = null;
			}
		}
	},false);
}

DockPanel.prototype.dropPanel = function(panel, mode, idx){
	with(this){
		if (holder.numChild==1 || this.holder == panel.holder){
			holder.addPanel(panel, mode, idx);
		}else{
		    //TODO: create new holder here
		    var newHolder = new DockHolder(null);
		    holder.replacePanel(newHolder, this);
		    newHolder.addPanel(this);
		    dropPanel(panel, mode, idx);
		}
	}
}

DockPanel.prototype.setPosSize = function(x, y, w, h){
	with(this.holder.elem.style){
		left = x;
		top = y;
		width = w;
		height = h;
	}
}

var _Dock = {
	panels: {},
	addPanel: function(panel){
		this.panels[panel.elem.id] = panel;
	},
	removePanel: function(panel){
		delete this.panels[panel.elem.id];
	},
	getPanel: function(elem){
		return this.panels[elem.id];
	}
}

DockHolder.saveFunc = function(codeData){
	var rootHolder = codeData.rootHolder;
	var panels = codeData.panels;
	
	var ret = {};
	var recur = function(holder, obj){
		if (obj instanceof DockHolder){
			obj.type = 'DockHolder';
			obj.curMode = holder.curMode;
			obj.percent = holder.getSpliter();
			obj.first = {};
			recur(holder.first, obj.first);			
			obj.second = {};
			recur(holder.second, obj.second);
		}else{
			obj.type = 'DockPanel';
			obj.id = holder.elem.id;
		}
	}
	recur(rootHolder, ret);
	return ret;
}


DockHolder.saveFunc = function(){
	var ret = {};
	var recur = function(holder, obj){
		if (holder instanceof DockHolder){
			obj.type = 'DockHolder';
			obj.curMode = holder.curMode;
			obj.percent = holder.getSpliter();
			obj.numChild = holder.numChild;
			if (holder.first){
				obj.first = {};
				recur(holder.first, obj.first);							
			}
			if (holder.second){
				obj.second = {};
				recur(holder.second, obj.second);				
			}
		}else{
			obj.type = 'DockPanel';
			obj.id = holder.elem.id;
		}
	}
	recur(_rootHolder, ret);
	return ret;
}

DockHolder.loadFunc = function(data){
	var processChild = function(idx, data, obj){
		var child = idx==0?data.first:data.second;
		if (child.type=='DockHolder'){
			var newHolder = new DockHolder(obj.elem);
			obj.addPanel(newHolder, obj.curMode, idx);
		}else{
			obj.addPanel(_Dock.panels[child.id], obj.curMode, idx);
		}
	}
	var recur = function(data, obj){
		obj.curMode = data.curMode;
		for (var i=0; i<data.numChild; i++){
			processChild(i, data, obj);
		}
		for (var idx=0;idx<data.numChild; idx++){
			var child = idx==0?data.first:data.second;
			if (child.type=='DockHolder'){
				recur(child, idx==0?obj.first:obj.second);				
			}
		}
		if (data.numChild>1)
			obj.setSpliter(data.percent);
	}
	/*
	if (rootHolder.second)
		rootHolder.detachPanel(rootHolder.second);
	if (rootHolder.first)
		rootHolder.detachPanel(rootHolder.first);
	_root.appendChild(rootHolder.elem);
	recur(data, rootHolder);
	//*/
	//*
	_root.removeChild(_rootHolder.elem);
	_rootHolder = new DockHolder(_root);
	recur(data, _rootHolder);
	//*/
}
//logpush('Finish processing DockPanel');